Strict

Import mojo
Import agfx

Class TVirtualDisplay

	Public

	'Device size
	Field DEVICE_WIDTH:Int
	Field DEVICE_HEIGHT:Int
	
	' Screen size
	Field SCREEN_WIDTH:Float
	Field SCREEN_HEIGHT:Float
	
	' VirtualScreen size
	Field VSCREEN_WIDTH:Float
	Field VSCREEN_HEIGHT:Float

	' Screen half size
	Field SCREEN_HALF_WIDTH:Float
	Field SCREEN_HALF_HEIGHT:Float
	
	' Screen half size
	Field VSCREEN_HALF_WIDTH:Float
	Field VSCREEN_HALF_HEIGHT:Float
	Field use_virtual_size:Bool = True
	
	Field SCREENX_RATIO:Float = 1.0
	Field SCREENY_RATIO:Float = 1.0

	Private
	
	Field vwidth:Float					' Virtual width
	Field vheight:Float					' Virtual height

	Field vratio:Float					' Virtual ratio

	Field scaledw:Float					' Width of *scaled* virtual display in real pixels
	Field scaledh:Float					' Width of *scaled* virtual display in real pixels

	Field widthborder:Float				' Size of border at sides
	Field heightborder:Float				' Size of border at top/bottom

	Field multi:Float						' Ratio scale factor
	Field vzoom:Float						' Zoom scale factor
	
	Field fdw:Float						' DeviceWidth gets pre-cast to Float in UpdateVirtualDisplay
	Field fdh:Float						' DeviceHeight gets pre-cast to Float in UpdateVirtualDisplay
	
	Public
	
	Method New (width:Int=640, height:Int=480, zoom:Float=1.0)

		' Set virtual width and height...
			
		vwidth = width
		vheight = height

		vzoom = zoom
		
		' Store ratio...
		
		vratio = vheight / vwidth

	End

	Method VDeviceWidth:Float()
		Return Self.vwidth
	End

	Method VDeviceHeight:Float()
		Return Self.vheight
	End


	Method SetVirtualSize:Void(width:Int=640, height:Int=480, zoom:Float=1.0)

		' Set virtual width and height...
			
		vwidth = width
		vheight = height

		vzoom = zoom
		
		' Store ratio...
		
		vratio = vheight / vwidth

		Self.DEVICE_WIDTH = DeviceWidth()
		Self.DEVICE_HEIGHT = DeviceHeight()

		Self.SCREEN_WIDTH = Float(Self.DEVICE_WIDTH)
		Self.SCREEN_HEIGHT = Float(Self.DEVICE_HEIGHT)
		Self.SCREEN_HALF_WIDTH = Self.SCREEN_WIDTH / 2.0
		Self.SCREEN_HALF_HEIGHT = Self.SCREEN_HEIGHT / 2.0

		Self.VSCREEN_WIDTH = Float(vwidth)
		Self.VSCREEN_HEIGHT = Float(vheight)
		Self.VSCREEN_HALF_WIDTH = Self.VSCREEN_WIDTH / 2.0
		Self.VSCREEN_HALF_HEIGHT = Self.VSCREEN_HEIGHT / 2.0

		'#If TARGET="android" Or TARGET="ios" Then
		'	Self.VirtualizeScreenSize(False)
		'#Else
			Self.VirtualizeScreenSize(True)
		'#End

		Self.SCREENX_RATIO = Self.DEVICE_WIDTH/Self.VSCREEN_WIDTH
		Self.SCREENY_RATIO = Self.DEVICE_HEIGHT/Self.VSCREEN_HEIGHT
	End

	Method GetZoom:Float ()
		Return vzoom
	End

	Method GetRatio:Float ()
		Return vratio
	End
	
	Method SetZoom:Void(zoomlevel:Float=1.0)
		If zoomlevel < 0.0 Then zoomlevel = 0.0
		vzoom = zoomlevel
	End
	
	Method AdjustZoom:Void(amount:Float)
		vzoom = vzoom + amount
		If vzoom < 0.0 Then vzoom = 0.0
	End
	
	Method VMouseX:Float(limit:Bool=True)
		Local mouseoffset:Float = MouseX() - Float (DeviceWidth()) * 0.5
		Local x:Float = (mouseoffset / multi) / vzoom + (Self.VDeviceWidth() * 0.5)

		If limit
			Local widthlimit:Float = vwidth - 1
			If x > 0
				If x < widthlimit
					Return x
				Else
					Return widthlimit
				Endif
			Else
				Return 0
			Endif
		Else
			Return x
		Endif
		Return 0
	End

	Method VMouseY:Float (limit:Bool=True)
	
		' Position of mouse, in real pixels, from centre of screen (centre being 0)...

		Local mouseoffset:Float = MouseY() - Float (DeviceHeight()) * 0.5
		
		' This calculates the scaled position on the virtual display. Somehow...

		Local y:Float = (mouseoffset / multi) / vzoom + (Self.VDeviceHeight() * 0.5)
		
		' Check if mouse is to be limited to virtual display area...

		If limit
		
			Local heightlimit:Float = vheight - 1
		
			If y > 0
				If y < heightlimit
					Return y
				Else
					Return heightlimit
				Endif
			Else
				Return 0
			Endif

		Else
			Return y
		Endif
		
		Return 0
	End
	
	Method VTouchX:Float(i:Int=0,limit:Bool=True)
		
		' Position of touch, in real pixels, from centre of screen (centre being 0)...
		
		Local mouseoffset:Float = TouchX(i) - Float (DeviceWidth()) * 0.5
		
		' This calculates the scaled position on the virtual display. Somehow...
		
		Local x:Float = (mouseoffset / multi) / vzoom + (Self.VDeviceWidth() * 0.5)

		' Check if mouse is to be limited to virtual display area...
		
		If limit
	
			Local widthlimit:Float = vwidth - 1
	
			If x > 0
				If x < widthlimit
					Return x
				Else
					Return widthlimit
				Endif
			Else
				Return 0
			Endif
	
		Else
			Return x
		Endif
		
		Return 0
	End

	Method VTouchY:Float (i:Int=0, limit:Bool=True)
	
		' Position of touch, in real pixels, from centre of screen (centre being 0)...

		Local mouseoffset:Float = TouchY(i) - Float (DeviceHeight()) * 0.5
		
		' This calculates the scaled position on the virtual display. Somehow...

		Local y:Float = (mouseoffset / multi) / vzoom + (Self.VDeviceHeight() * 0.5)
		
		' Check if mouse is to be limited to virtual display area...

		If limit
		
			Local heightlimit:Float = vheight - 1
		
			If y > 0
				If y < heightlimit
					Return y
				Else
					Return heightlimit
				Endif
			Else
				Return 0
			Endif

		Else
			Return y
		Endif
		
		Return 0
	End

	Method VirtualizeScreenSize:Void(vs:Bool)
		Self.use_virtual_size = vs
	End Method
	
	
	Method IsVirtualScaleUsed:Bool()
		Return Self.use_virtual_size
	End Method
	
	Method UpdateVirtualDisplay:Void(zoomborders:Bool=True, keepborders:Bool=True)

		' Store device resolution as float values to avoid loads of casts. Doing it here as
		' device resolution may potentially be changed on the fly on some platforms...
		
		fdw = Float (DeviceWidth())
		fdh = Float (DeviceHeight())
		
		' Device ratio is calculated on the fly since it can change (eg. resizeable
		' browser window)...
		
		Local dratio:Float = fdh / fdw
		
		' Compare to pre-calculated virtual device ratio...
		
		If dratio >= vratio

			' -----------------------------------------------------------------
			' Device aspect narrower than (or same as) game aspect ratio:
			' will use full width, borders above and below...
			' -----------------------------------------------------------------

			' Multiplier required to scale game width to device width (to be applied to height)...
			
			multi = fdw / vwidth
			
			' "vheight * multi" below applies width multiplier to height...
			
			heightborder = (fdh - vheight * multi) * 0.5
			widthborder = 0
			
		Else

			' -----------------------------------------------------------------
			' Device aspect wider than game aspect ratio:
			' will use full height, borders at sides...
			' -----------------------------------------------------------------
			
			' Multiplier required to scale game height to device height (to be applied to width)...
			
			multi = fdh / vheight
			
			' "vwidth * multi" below applies height multiplier to width...

			widthborder = (fdw - vwidth * multi) * 0.5
			heightborder = 0

		Endif
		
		' ---------------------------------------------------------------------
		' Clear outer area (black borders if required)...
		' ---------------------------------------------------------------------
		
		SetScissor 0, 0, DeviceWidth(), DeviceHeight()
		Cls 0, 0, 0
		
		' ---------------------------------------------------------------------
		' Set inner area...
		' ---------------------------------------------------------------------

		Local sx:Float, sy:Float, sw:Float, sh:Float

		If zoomborders

			' Width/height of SCALED virtual display in real pixels...
			
			Local realx:Float = (vwidth * vzoom * multi)
			Local realy:Float = (vheight * vzoom * multi)
	
			' Space in pixels between real device borders and virtual device borders...
			
			Local offx:Float = (fdw - realx) * 0.5
			Local offy:Float = (fdh - realy) * 0.5

			' WIP: Retain borders when zoomed in... add option to lose borders...
			' Can keepborders check go outside?
			
			If keepborders

				If offx < widthborder
					sx = widthborder
					sw = fdw - widthborder * 2.0
				Else
					sx = offx
					sw = fdw - (offx * 2.0)
				Endif

			Else

				sx = offx
				sw = fdw - (offx * 2.0)

			Endif
			
			If keepborders
	
				If offy < heightborder
					sy = heightborder
					sh = fdh - heightborder * 2.0
				Else
					sy = offy
					sh = fdh - (offy * 2.0)
				Endif

			Else

				sy = offy
				sh = fdh - (offy * 2.0)

			Endif
			
			sx = Max (0.0, sx)
			sy = Max (0.0, sy)
			sw = Min (sw, fdw)
			'sh = Min (sh, fdw)
			sh = Min (sh, fdh)
			
			SetScissor sx, sy, sw, sh
			
		Else

			sx = Max (0.0, widthborder)
			sy = Max (0.0, heightborder)
			sw = Min (fdw - widthborder * 2.0, fdw)
			'sh = Min (fdh - heightborder * 2.0, fdw)
			sh = Min (fdh - heightborder * 2.0, fdh)

			SetScissor sx, sy, sw, sh

		Endif
		
		' ---------------------------------------------------------------------
		' Scale and translate everything...
		' ---------------------------------------------------------------------
		
		Scale multi * vzoom, multi * vzoom

		' ---------------------------------------------------------------------
		' Shift display to account for borders/zoom level...
		' ---------------------------------------------------------------------

		If vzoom ' Gets skipped if zero...
		
			' Width and height of *scaled* virtual display in pixels...

			scaledw = (vwidth * multi * vzoom)
			scaledh = (vheight * multi * vzoom)

			' Find offsets by which view needs to be shifted...
			
			Local xoff:Float = (fdw - scaledw) * 0.5
			Local yoff:Float = (fdh - scaledh) * 0.5

			' Ahh, good old trial and error -- I have no idea how this works!
			
			xoff = (xoff / multi) / vzoom
			yoff = (yoff / multi) / vzoom
			
			' Aaaand, shift...
			
			Translate xoff, yoff
		
		Endif
		
	End

End

